"""Package containing the atom widgets of the DSL."""

import widget
import expressions
from PyQt4 import QtGui, QtCore

class Atom(widget.Widget):
  def __init__(self, corwin__bgcolor = None, corwin__enabled = None, *args, **kwargs):
    super(Atom, self).__init__(*args, **kwargs)
    self._bgcolor = corwin__bgcolor
    self._enabled = corwin__enabled
    self._done = 0
    self.w = None

  # Create the QT widget
  def createQTWidget(self, window):
    self.w = QtGui.QWidget(window)
    self.createScrollArea(window)

  def createScrollArea(self, window):
    self.s = QtGui.QScrollArea(window)
    self.s.setFrameShape(QtGui.QFrame.NoFrame)
    self.s.setWidget(self.w)

  def update(self, old, new):
    self._updateAll(PosNSize = False)

  # Move & resize the atom
  def _updatePositionAndSize(self):
    self._done = 1
    stop, sright, sbottom, sleft = self.get_box_edges()
    self.s.move(sleft,stop)
    self.s.resize(sright-sleft, sbottom-stop)
    top, right, bottom, left = self.get_widget_edges()
    if self._done:
      self.w.move(left-sleft,top-stop)
    else:
      self.w.move(left,top)
    self.w.resize(right-left, bottom-top)

  def _updateBGColor(self):
    if self._bgcolor:
      self.w.setStyleSheet("QWidget { background-color: *%*COLOR*%*; }".replace("*%*COLOR*%*", self._fixColor(str(self._bgcolor.calculateValue()))))

  def _updateEnabled(self):
    if self._enabled:
      if self._enabled.calculateValue() == False:
        self.w.setDisabled(True)

  def _fixColor(self, color_str):
    if color_str[:2] == "0x":
      color_str = color_str[2:]
    if len(color_str) == 6:
        if not(0 in [x in "0123456789abcdefABCDEF" for x in color_str]):
          return "rgb(%s, %s, %s)" % (str(int(color_str[4:6],16)), str(int(color_str[2:4],16)), str(int(color_str[:2],16)))
    return color_str

  def _readTuple(self, exp):
    return tuple(x.strip() for x in str(exp.calculateValue()).replace("(","").replace(")","").split(","))

  def _evaluateToValue(self, expression, value):
    if (expression and (not expression.isReadonly())):
      if expression.calculateValue() != value:
        expression.evaluateTo(value, (self.update,))
    

  def _updateAll(self, PosNSize = True):
    if PosNSize:
      self._updatePositionAndSize()
    self._updateBGColor()
    self._updateEnabled()
        
  def draw(self, window):
    super(Atom, self).draw(window)
    if self.w is None:
      self.createQTWidget(window)
    self._updateAll()
    self.w.show()


class AtomWithText(Atom):
  def __init__(self, corwin__text=None, corwin__fgcolor = None, corwin__font = None, *args, **kwargs):
    super(AtomWithText, self).__init__(*args, **kwargs)
    self._text = corwin__text
    self._fgcolor = corwin__fgcolor
    self._font = corwin__font
    self._plainText = False
  
  def _updateText(self):
    if self._text != None:
        if self._textChanged():
          if self._plainText:
            self.w.setPlainText(str(self._text.calculateValue()))
          else:
            self.w.setText(str(self._text.calculateValue()))
    else:
        self.w.setText("")

  def _textChanged(self):
    # If we didn't override this in the specific class, then just assume text has changed
    return True

  def getTextDims(self):
    if self._text is None:
      return 0, 0
    fm = QtGui.QFontMetrics(self.w.font())
    lines = self._text.calculateValue().split("\n")
    return (max([fm.width(x) for x in lines]) + 5,(fm.height() + 2 )* len(lines))

  def _updateFGColor(self):
    if self._fgcolor:
      self.w.setStyleSheet("QWidget { color: *%*COLOR*%*; }".replace("*%*COLOR*%*", self._fixColor(str(self._fgcolor.calculateValue()))))

  def _updateFont(self):
    if self._font:
      font_info = self._readTuple(self._font)
      font = QtGui.QFont(font_info[0], int(font_info[1].replace("px","")))
      if "bold" in font_info[2].lower():
        font.setWeight(75)
      if "italic" in font_info[2].lower():
        font.setItalic(True)
      self.w.setFont(font)

  # Update the text in the QT Widget
  def _updateAll(self, PosNSize = True):
    super(AtomWithText, self)._updateAll()
    self._updateText()
    self._updateFGColor()
    self._updateFont()
    
class AtomWithAlignment(AtomWithText):
  def __init__(self, corwin__halign = None, corwin__valign = None, *args, **kwargs):
    super(AtomWithAlignment, self).__init__(*args, **kwargs)
    self._halign = corwin__halign
    self._valign = corwin__valign
  
  def _updateAll(self, PosNSize = True):
    super(AtomWithAlignment, self)._updateAll()
    self._updateAlignment()
  
  def _updateAlignment(self):
    alignment = QtCore.Qt.Alignment()
    if self._halign:
      halign = str(self._halign.calculateValue())
      if ("right" in halign):
        alignment = alignment | QtCore.Qt.AlignRight
      elif ("center" in halign):
        alignment = alignment | QtCore.Qt.AlignHCenter
      else:
        alignment = alignment | QtCore.Qt.AlignLeft
    else:
      alignment = alignment | QtCore.Qt.AlignLeft
    if self._valign:
      valign = str(self._valign.calculateValue())
      if ("bottom" in valign):
        alignment = alignment | QtCore.Qt.AlignBottom
      elif ("center" in valign):
        alignment = alignment | QtCore.Qt.AlignVCenter
      else:
        alignment = alignment | QtCore.Qt.AlignTop
    else:
      alignment = alignment | QtCore.Qt.AlignTop
    self.w.setAlignment(alignment)


    # Should account for this size of the text in the label...
class Label(AtomWithAlignment):

  def __init__(self, *args, **kwargs):
    super(Label,self).__init__(*args, **kwargs)
    self._setWidth = False
    self._setHeight = False
    super(Label, self).update_size(expressions.Constant(0), expressions.Constant(0))
    self.is_updating = False

  # Create the QT Label
  def createQTWidget(self, window):
    self.w = QtGui.QLabel(window)
    self.createScrollArea(window)

  def _textChanged(self):
    return self.w.text() != str(self._text.calculateValue())

  def _updateText(self):
    if self.is_updating:
      return
    self.is_updating = True
    super(Label,self)._updateText()
    if self._setWidth:
      new_width = None
    else:
      new_width = expressions.Constant(self.getTextDims()[0])
    if self._setHeight:
      new_height = None
    else:
      new_height = expressions.Constant(self.getTextDims()[1])

    width, height = self.calculate_size()
    if ((new_width and new_width.calculateValue() != width) or
        (new_height and new_height.calculateValue() != height)):
      super(Label, self).update_size(new_width, new_height)
      expressions.redraw_function()
    self.is_updating = False

  def recalculate_size(self):
    self._box_size = [None, None]
    super(Label, self).update_size(*self._size)

  def update_size(self, width, height):
    self._setWidth = width is not None
    self._setHeight = height is not None
    super(Label, self).update_size(width, height)


class TextView(AtomWithAlignment):
  def createQTWidget(self, window):
    self.w = QtGui.QTextEdit(window)
    self.w.setReadOnly(True)
    #self.w.setStyleSheet("QWidget { background-color: gray; }".replace("*%*COLOR*%*", self._fixColor(str(self._bgcolor.calculateValue()))))
    self.createScrollArea(window)
    self.w.setLineWrapMode(QtGui.QTextEdit.NoWrap)

  def _textChanged(self):
    return self.w.toPlainText() != str(self._text.calculateValue())


# TODO make sure text changes size if needed
class TextBox(AtomWithAlignment):
  def __init__(self, corwin__selected=None, *args, **kwargs):
    super(TextBox, self).__init__(*args, **kwargs)
    self._selection = corwin__selected

  def createQTWidget(self, window):
    self.w = QtGui.QTextEdit(window)
    self._plainText = True
    if self._text:
      if self._text.isReadonly():
        self.w.setReadOnly(True)
      else:
        self.w.textChanged.connect(self._reevaluate)
        self.w.selectionChanged.connect(self._reevaluate)
    else:
      self.w.setReadOnly(True)
    self.w.setLineWrapMode(QtGui.QTextEdit.NoWrap)
    self.createScrollArea(window)

  def _updateAll(self, PosNSize=True):
    if self._text and not self._text.isReadonly():
      self.w.textChanged.disconnect(self._reevaluate)
      self.w.selectionChanged.disconnect(self._reevaluate)
    super(TextBox, self)._updateAll()
    self._updateSelection()
    if self._text and not self._text.isReadonly():
      self.w.textChanged.connect(self._reevaluate)
      self.w.selectionChanged.connect(self._reevaluate)

  def _updateSelection(self):
    if self._selection:
      selection_values = self._readTuple(self._selection)
      cursor = self.w.textCursor()
      cursor.setPosition(int(selection_values[0]), QtGui.QTextCursor.MoveAnchor)
      cursor.setPosition(int(selection_values[1]), QtGui.QTextCursor.KeepAnchor)
      self.w.setTextCursor(cursor)

  def _reevaluate(self):
    self._evaluateToValue(self._text, str(self.w.toPlainText()))
    self._evaluateToValue(self._selection, "(%i,%i)" % (self.w.textCursor().anchor(), self.w.textCursor().position()) )

  def _textChanged(self):
    return self.w.toPlainText() != str(self._text.calculateValue())

class Button(AtomWithText):
  def __init__(self, corwin__image = None, corwin__value = None, *args, **kwargs):
    super(Button, self).__init__(*args, **kwargs)
    self._icon = corwin__image
    self._value = corwin__value

  # Create the QT Label
  def createQTWidget(self, window):
    self.w = QtGui.QPushButton(window)
    if self._value:
      if self._value.isReadonly():
        self.w.setDisabled(True)
      else:
        self.w.pressed.connect(self._evaluatePressed)
        self.w.released.connect(self._evaluateReleased)
    else:
      self.w.setDisabled(True)
    self.createScrollArea(window)

  def _updateAll(self, PosNSize = True):
    super(Button, self)._updateAll()
    self._updateIcon()

  def _updateIcon(self):
    if self._icon:
      self.w.setIcon(QtGui.QIcon(str(self._icon.calculateValue())))

  def _evaluatePressed(self):
    self._evaluateToValue(self._value, 1)

  def _evaluateReleased(self):
    self._evaluateToValue(self._value, 0)


class EmptyBox(Atom):
  # Create the QT Label
  def createQTWidget(self, window):
    self.w = QtGui.QLabel(window)
    self.createScrollArea(window)

class Radio(AtomWithText):
  def __init__(self, corwin__checked=None, *args, **kwargs):
    super(Radio, self).__init__(*args, **kwargs)
    self.checked = corwin__checked

  # Create the QT widget
  def createQTWidget(self, window):
    self.button_group = QtGui.QButtonGroup(parent=window)
    self.w = QtGui.QRadioButton(window)
    self.button_group.addButton(self.w)
    self.button_group.setExclusive(False)

    self.w.setCheckable(True)
    self.w.setEnabled(True)
    if self.checked:
      if self.checked.isReadonly():
        self.w.setCheckable(False)
        self.w.setEnabled(False)
      else:
        self.w.clicked.connect(self._reevaluate)
    else:
      self.w.setCheckable(False)
      self.w.setEnabled(False)
    self.createScrollArea(window)

  def _updateAll(self, PosNSize = True):
    super(Radio, self)._updateAll()
    self._updateChecked()

  def _updateChecked(self):
    if self.checked:
      self.w.setChecked(self.checked.calculateValue())

  def _reevaluate(self):
    self._evaluateToValue(self.checked, self.w.isChecked())

    
class CheckBox(AtomWithText):
  def __init__(self, corwin__checked=None, *args, **kwargs):
    super(CheckBox, self).__init__(*args, **kwargs)
    self.checked = corwin__checked

  # Create the QT widget
  def createQTWidget(self, window):
    self.w = QtGui.QCheckBox(window)
    self.w.setCheckable(True)
    self.w.setEnabled(True)
    if self.checked:
      if self.checked.isReadonly():
        self.w.setCheckable(False)
        self.w.setEnabled(False)
      else:
        self.w.stateChanged.connect(self._reevaluate)
    else:
      self.w.setCheckable(False)
      self.w.setEnabled(False)
    self.createScrollArea(window)

  def _updateAll(self, PosNSize = True):
    super(CheckBox, self)._updateAll()
    self._updateChecked()

  def _updateChecked(self):
    if self.checked:
      self.w.setChecked(self.checked.calculateValue())

  def _reevaluate(self):
    self._evaluateToValue(self.checked, self.w.isChecked())

    
class Image(Atom):
  def __init__(self, corwin__image=None, corwin__scale_to_size = False, *args, **kwargs):
    super(Image, self).__init__(*args, **kwargs)
    self.image = corwin__image
    self.scale_to_size = corwin__scale_to_size
    self._setWidth = False
    self._setHeight = False
    super(Image, self).update_size(expressions.Constant(0), expressions.Constant(0))
    self.is_updating = False

    # Create the QT widget
  def createQTWidget(self, window):
    self.w = QtGui.QLabel(window)
    self.p = QtGui.QPixmap(self.image)
    self.createScrollArea(window)

  def _updateAll(self, PosNSize = True):
    super(Image, self)._updateAll()
    self._updateImage()

  def _updateImage(self):
    if self.is_updating:
      return
    self.is_updating = True
    self.p.load(str(self.image.calculateValue()))
    if self.scale_to_size:
      top, right, bottom, left = self.get_widget_edges()
      self.p = self.p.scaled(right-left, bottom-top)
    self.w.setPixmap(self.p)

    from PIL import Image as ImageStuff
    im = ImageStuff.open(self.image.calculateValue())
    if self._setWidth:
      new_width = None
    else:
      new_width = expressions.Constant(im.size[0])
    if self._setHeight:
      new_height = None
    else:
      new_height = expressions.Constant(im.size[1])

    width, height = self.calculate_size()
    if ((new_width and new_width.calculateValue() != width) or
        (new_height and new_height.calculateValue() != height)):
      super(Image, self).update_size(new_width, new_height)
    expressions.redraw_function()
    self.is_updating = False

  def recalculate_size(self):
    self._box_size = [None, None]
    super(Image, self).update_size(*self._size)

  def update_size(self, width, height):
    self._setWidth = width is not None
    self._setHeight = height is not None
    super(Image, self).update_size(width, height)


class ComboBox(Atom):
  def __init__(self, corwin__value = None, corwin__options = None, *args, **kwargs):
    super(ComboBox, self).__init__(*args, **kwargs)
    self._chosen = corwin__value
    self._options_str = corwin__options

  def createQTWidget(self, window):
    self.w = QtGui.QComboBox(window)
    self._options = str(self._options_str.calculateValue()).split(',')
    for o in self._options:
      self.w.addItem(o)
    if self._chosen:
      self.w.setCurrentIndex(self._chosen.calculateValue())
      if self._chosen.isReadonly():
        self.w.setDisabled(True)
      else:
        self.w.activated.connect(self._reevaluate)
    else:
      self.w.setDisabled(True)
    self.createScrollArea(window)

  def _reevaluate(self):
    self._evaluateToValue(self._chosen, self.w.currentIndex())

  def _updateAll(self, PosNSize = True):
    super(ComboBox, self)._updateAll()
    self._updateChosen()

  def _updateChosen(self):
    if self._chosen:
      self.w.setCurrentIndex(self._chosen.calculateValue())
    
        
class Slider(Atom):
  def __init__(self, corwin__orientation = None, corwin__value = None, corwin__minvalue = None, corwin__maxvalue = None, *args, **kwargs):
    super(Slider, self).__init__(*args, **kwargs)
    self._value = corwin__value
    self._minvalue = corwin__minvalue
    self._maxvalue = corwin__maxvalue
    self._orientation = corwin__orientation

  def createQTWidget(self, window):
    if self._orientation and ("vertical" in self._orientation.calculateValue().lower()):
      orientation = QtCore.Qt.Orientation(2)
    else:
      orientation = QtCore.Qt.Orientation(1)
    self.w = QtGui.QSlider(orientation = orientation, parent = window)
    self.w.setMinimum(self._minvalue.calculateValue())
    self.w.setMaximum(self._maxvalue.calculateValue())
    if self._value:
      self.w.setSliderPosition(self._value.calculateValue())
      if self._value.isReadonly():
        self.w.setDisabled(True)
      else:
        self.w.sliderMoved.connect(self._reevaluate)
    else:
      self.w.setDisabled(True)
    self.createScrollArea(window)

  def _reevaluate(self):
    self._evaluateToValue(self._value, self.w.value())

    
